package com.photoeditor.demo.model.store.download;

import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

import com.photoeditor.demo.model.store.module.ServerUrlUpdater;
import com.photoeditor.demo.model.store.module.StoreOnlineBean;
import com.photoeditor.demo.statistics.StatisticsConstant;
import com.photoeditor.demo.util.AndroidDevice;
import com.photoeditor.demo.util.RegionUtil;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.CopyOnWriteArrayList;

public class Downloader<T extends Serializable> {
    private String urlstr;// 下载的地址
    private String localFile;// 保存路径
    private int threadcount;// 线程数
    private Handler mHandler;// 消息处理器
    private int fileSize;// 所要下载的文件的大小
    private Context context;
    private CopyOnWriteArrayList<DownloadInfo> infos;// 存放下载信息类的集合
    private static final int INIT = 1;//定义三种下载的状态：初始化状态，正在下载状态，暂停状态
    private static final int DOWNLOADING = 2;
    private static final int PAUSE = 3;
    private int state = INIT;

    private T mContentInfoBO;

    private int mType;

    public Downloader(String urlstr, String localfile, int threadcount,
            Context context, Handler mHandler , T contentInfoBO , int type) {
        this.urlstr = urlstr;
        this.localFile = localfile;
        this.threadcount = threadcount;
        this.mHandler = mHandler;
        this.context = context;
        this.mType = type;
        this.mContentInfoBO = contentInfoBO;
    }
    /**
     *判断是否正在下载
     */
    public boolean isdownloading() {
        return state == DOWNLOADING;
    }
    /**
     * 得到downloader里的信息
     * 首先进行判断是否是第一次下载，如果是第一次就要进行初始化，并将下载器的信息保存到数据库中
     * 如果不是第一次下载，那就要从数据库中读出之前下载的信息（起始位置，结束为止，文件大小等），并将下载信息返回给下载器
     */
    public LoadInfo getDownloaderInfors() {

       init();
       int range = fileSize / threadcount;
       infos = new CopyOnWriteArrayList<>();
       for (int i = 0; i < threadcount - 1; i++) {
           DownloadInfo info = new DownloadInfo(i, i * range, (i + 1) * range- 1, 0, urlstr);
           infos.add(info);
       }
       DownloadInfo info = new DownloadInfo(threadcount - 1, (threadcount - 1)* range, fileSize - 1, 0, urlstr);
       infos.add(info);

       // 创建一个LoadInfo对象记载下载器的具体信息
       LoadInfo loadInfo = new LoadInfo(fileSize, 0, urlstr);
       return loadInfo;

    }

    /**
     * 初始化
     */
    private void init() {
        try {

            URL url = new URL(urlstr);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(5000);
            connection.setRequestMethod("GET");
            fileSize = connection.getContentLength();

            File file = new File(localFile);
            if (!file.exists()) {
                if (!file.getParentFile().exists()) {
                    file.getParentFile().mkdirs();
                }
                file.createNewFile();
            }

            // 本地访问文件
            RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");
            accessFile.setLength(fileSize);
            accessFile.close();
            connection.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 利用线程开始下载数据
     */
    public void download() {
        if (infos != null) {
            if (state == DOWNLOADING)
                return;
            state = DOWNLOADING;
            for (DownloadInfo info : infos) {
                new MyThread(info.getThreadId(), info.getStartPos(),
                        info.getEndPos(), info.getCompeleteSize(),
                        info.getUrl()).start();
            }
        }
    }

    public class MyThread extends Thread {
        private int threadId;
        private int startPos;
        private int endPos;
        private int compeleteSize;

        /**
         * apk资源下载
         */
        private String downloadUrl;

        public MyThread(int threadId, int startPos, int endPos,
                int compeleteSize, String urlstr) {
            this.threadId = threadId;
            this.startPos = startPos;
            this.endPos = endPos;
            this.compeleteSize = compeleteSize;
            this.downloadUrl = urlstr;
        }
        @Override
        public void run() {
            HttpURLConnection connection = null;
            RandomAccessFile randomAccessFile = null;
            InputStream is = null;
            String country = RegionUtil.getSimCountry();
            String model = android.os.Build.BRAND + ":" + android.os.Build.MODEL;
            boolean isNetworkOkBeforeDownload = AndroidDevice.isNetworkOK(context);
            try {
                long startTime = System.currentTimeMillis();
                URL url = new URL(downloadUrl);
                connection = (HttpURLConnection) url.openConnection();
                connection.setConnectTimeout(20000);
                connection.setReadTimeout(20000);
                connection.setRequestMethod("GET");
                // 设置范围，格式为Range：bytes x-y;
                connection.setRequestProperty("Range", "bytes="+(startPos + compeleteSize) + "-" + endPos);

                randomAccessFile = new RandomAccessFile(localFile, "rwd");

                randomAccessFile.seek(startPos + compeleteSize);
                // 将要下载的文件写到保存在保存路径下的文件中
                is = connection.getInputStream();
                long connectLatency = System.currentTimeMillis() - startTime;
                int webFileLength = connection.getContentLength();
                int downloadedFileLength = 0;

                byte[] buffer = new byte[4096];
                int length = -1;
                while ((length = is.read(buffer)) != -1) {
                    randomAccessFile.write(buffer, 0, length);
                    compeleteSize += length;
                    downloadedFileLength += length;

                    // 用消息将下载信息传给进度条，对进度条进行更新
                    Message msg = mHandler.obtainMessage(1);

                    Bundle bundle = new Bundle();
                    bundle.putString("downloadUrl", downloadUrl);
                    bundle.putInt("compeleteSize", compeleteSize);
                    bundle.putInt("fileSize", fileSize);
                    bundle.putString("localFile", localFile);
                    bundle.putInt("type", mType);
                    bundle.putSerializable("contentInfoBO", mContentInfoBO);
                    msg.setData(bundle);

                    msg.sendToTarget();
                    if (state == PAUSE) {
                        return;
                    }
                }

                // 用消息将下载信息传给进度条，对进度条进行更新
                Message msg = mHandler.obtainMessage(1);

                Bundle bundle = new Bundle();
                bundle.putString("downloadUrl", downloadUrl);
                bundle.putInt("compeleteSize", fileSize);
                bundle.putInt("fileSize", fileSize);
                bundle.putString("localFile", localFile);
                bundle.putInt("type", mType);
                bundle.putSerializable("contentInfoBO", mContentInfoBO);
                msg.setData(bundle);

                msg.sendToTarget();
                long downloadDuration = System.currentTimeMillis() - startTime;
                float downloadSpeed = (float) (downloadedFileLength * 1.0 / 1024 / (downloadDuration * 1.0 / 1000));
                String event = downloadedFileLength == webFileLength ?
                        StatisticsConstant.UMENG_CUSTOM_EVENT_SPEED_TEST_SUCCESS :
                        StatisticsConstant.UMENG_CUSTOM_EVENT_SPEED_TEST_INCORRECT;
                if (isNetworkOkBeforeDownload) {
                    com.common.statistics.StatisticsUtils.statisics(event,
                            "",
                            (int) downloadSpeed,
                            "speed", String.valueOf((int) downloadSpeed / 10),
                            "country", country,
                            "model", model,
                            "connectLatency", String.valueOf(connectLatency / 10),
                            "network", AndroidDevice.getNetworkTypeName(),
                            "package-name", ((StoreOnlineBean) mContentInfoBO).getPkgName(),
                            "url", StoreOnlineBean.removeQueryString(downloadUrl));
                }
            } catch (Exception e) {
                // 发送失败回调
                Message msg = mHandler.obtainMessage(2);
                Bundle bundle = new Bundle();
                bundle.putSerializable("contentInfoBO", mContentInfoBO);
                bundle.putInt("type", mType);
                msg.setData(bundle);
                msg.sendToTarget();
                if (AndroidDevice.isNetworkOK(context)) {
                    ServerUrlUpdater.getInstance().getNewestServerUrl();
                }
                if (isNetworkOkBeforeDownload) {
                    com.common.statistics.StatisticsUtils.statisics(StatisticsConstant.UMENG_CUSTOM_EVENT_SPEED_TEST_ERROR,
                            "",
                            0,
                            "error-msg", e.getMessage(),
                            "country", country,
                            "model", model,
                            "network", AndroidDevice.getNetworkTypeName(),
                            "package-name", ((StoreOnlineBean)mContentInfoBO).getPkgName(),
                            "url", StoreOnlineBean.removeQueryString(downloadUrl));
                }
            } finally {
                if (is != null) {
                    try {
                       is.close();
                   } catch (IOException e) {
                   }
                }
                if (connection != null) {
                   try {
                        connection.disconnect();
                   } catch (Throwable tr) {
                   }
                }
            }
        }
    }

    //设置暂停
    public void pause() {
        state = PAUSE;
    }
    //重置下载状态
    public void reset() {
        state = INIT;
    }
}